fix(bench): give in-process its own drain byte cap - #179
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
shove-docs | cc8923c | Commit Preview URL Branch Preview URL |
Sep 09 2026, 02:16 PM |
At the matrix's 3 GiB the 64 KiB leg is 49 152 messages, and the 2026-09-08 run drained every one of the in-process 64 KiB cells in 0.09-0.46 s. That is under MIN_FRAMEWORK_WINDOW_SECS, so the harness marked all eleven rows setup_bound and the charts withheld all eleven and captioned why: the fastest backend in the set publishes no 64 KiB consume rate at all, and 99 of the document's 822 rows are setup_bound. 3 GiB is not a cautious number for the other five, it is the containerised limit -- they stage the corpus inside the 8 GB Docker VM, where 3 GiB is already over a third of it and where a 3.2 GB backlog has already taken Redis down mid-pass. Raising the cap globally would buy the 64 KiB leg at the price of the VM. In-process stages its corpus in the harness process on the 64 GB host and never starts a container, so it is the one backend whose cap can rise without touching the VM. Substitute --drain-max-bytes for the inmemory target only, at 32 GiB, the mirror of the SQS corpus deviation already here: SQS drains a smaller corpus because it is the only backend slow enough that the pinned count means something else, in-process a larger one because it is the only one fast enough that the pinned byte cap does. Factor both substitutions through substitute_knob so a deviation still dies when its knob leaves the matrix. 32 GiB is 524 288 messages at 64 KiB. Projecting the measured rates onto that corpus puts ten of the eleven cells at 1.0-5.0 s and the eleventh (consumer_group at two consumers, 514 k msg/s) at ~0.9 s, so the leg publishes instead of being withheld whole; a cell under the floor is still withheld and captioned. It also puts the 1 KiB leg back on the pinned 6 000 000 from 3 145 728, clearing the one cell that drained in 0.97 s. Rendering control: projecting that corpus onto the committed six-backend document and running chartgen drops the "inmemory / parallel, batch: measured, but every window under 1 s" note and adds a two-line "corpus differs by backend: inmemory 524k; kafka, nats, rabbitmq, redis, sqs 49k" group. The 64 KiB parallel-vs-sequenced canvas goes 672 -> 688 of the 768 that MAX_CAPTION_GROWTH_LINES allows, so three of eight growth lines are spent and five stay spare. Script and runbook only; no document or SVG churn until the rerun.
zannis
force-pushed
the
zannis/fix/raise-large-payload-drain-corpus
branch
from
September 9, 2026 14:14
7ae6d0d to
cc8923c
Compare
zannis
added a commit
that referenced
this pull request
Sep 10, 2026
…ne the cap would produce (#196) Three prose claims outran the committed results document. No run, chart or results-document change here; the byte-compare in tests/chartgen.rs is unaffected. The 32 GiB in-process drain byte cap (#179) landed after the in-process leg in benches/results/bench-results.json was measured, and a leg only changes by being re-measured. The runbook nevertheless described the cap's effects in the present tense - "the leg publishes instead of being withheld whole", "32 GiB puts it back on the pinned count of 6 000 000" - which reads as a description of what is plotted. What is plotted is the 3 GiB shape: drain.corpus 49 152 at 64 KiB with all eleven rows setup_bound and withheld, and 3 145 728 at 1 KiB, which is what the chart captions name. Those effects are now stated as what the next in-process pass produces, with the published shape named alongside. The same section illustrated the corpus caption with "inmemory 524k", a string that appears in no committed chart. It now quotes the caption the committed charts actually carry. The "consumed before assembly" note counted four such failures with three of them RabbitMQ. The document carries two: in-process consume_batch (37 000 of 49 152) and RabbitMQ consume_parallel (46 467 of 49 152), both at 64 KiB with eight consumers. Corrected in the runbook and in the bench.sh comment it is derived from. The performance page's batching paragraph mixed an off-matrix batch-size probe (500/50/10-message flushes at 55k/80k/79k msg/s) with a document row (~356k for the parallel consumer) as if the four were one measurement, and quoted a number the charts withhold without saying so. Each side is now attributed, and the "below about 1 KiB the batch path leads" claim is replaced by what the document holds across its own payload axis: batch ahead 3-10x at 64 B and 2-7x at 1 KiB, behind 2-4x at 64 KiB.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
benches/results/bench-results.jsononmainholds six runs and 822 rows, and 99 of those rows aresetup_bound— measured, but withheld from every chart. The largest single block is the in-process 64 KiB consume leg: at the matrix's 3 GiB cap that leg is 49 152 messages, and the 2026-09-08 run drained every one of the eleven cells in 0.09–0.46 s. All eleven fall underMIN_FRAMEWORK_WINDOW_SECS, so the harness marks themsetup_boundand the charts withhold all eleven behind one caption line:Nothing false is published, but the fastest backend in the set contributes no 64 KiB consume rate at all.
framework)setup_bound)Why not just raise the cap
Because 3 GiB is not a cautious number for the other five — it is the containerised limit. Every other backend stages its corpus inside the 8 GB Docker VM (Redis and NATS in container memory, Kafka through its page cache), where 3 GiB is already over a third of the VM and where a 3.2 GB backlog has already taken Redis down mid-pass — the reason
--load-backlog-max-bytesexists at all.A global raise also lands on a leg that does not need it: past ~5.7 GiB the 1 KiB leg stops being byte-bound and doubles to the pinned 6 M for every backend, which projects to about +45 min of drain wall clock (RabbitMQ's 1 KiB leg alone goes 25.8 → 49.1 min) to fix rows that already clear the floor.
In-process is the one backend that stages its corpus in the harness process on the 64 GB host and never starts a container. So its cap is the one that can rise.
What this does
Substitutes
--drain-max-bytesfor theinmemorytarget only, at 32 GiB — the mirror image of the SQS corpus deviation already in the script. SQS drains a smaller corpus because it is the only backend slow enough that the pinned count means something else there; in-process is allowed a larger one because it is the only backend fast enough that the pinned byte cap does. Both substitutions now go through onesubstitute_knobhelper that still dies when its knob leaves the matrix.32 GiB is 524 288 messages at 64 KiB. Projecting the measured rates onto that corpus:
consume_parallel1/2/4/8consume_batch1/2/4consumer_group1/4/8consumer_group2setup_boundTen of eleven publish. The eleventh is the fastest cell in the document (514 k msg/s); clearing it too needs ~40 GiB, and
MIN_FRAMEWORK_CORPUS_MESSAGES's own doc declines to chase a window that hardware speed keeps moving. It stays withheld and captioned, which costs one bar rather than the pass.It moves the 1 KiB leg too, by design: 3 145 728 → the pinned 6 000 000, so in-process runs two of its three legs on the matrix's own corpus rather than one, and the single 1 KiB cell that drained in 0.97 s clears the floor. Cost: ~40 s and 6 GiB rather than 3 GiB resident. Peak resident is the 64 KiB leg at ~34 GB of 64 GB, with no container to share the host.
Verification
Argv, per backend — stubbed
cargo/docker, ran the script:inmemory→--drain-messages 6000000 --drain-max-bytes 34359738368kafka,redis→ both pinned values untouchedsqs→--drain-messages 60000, byte cap untouched at 3 GiB,--fifo-messages 100The guard fires — removed
--drain-max-bytesfromMATRIXand re-raninmemory:Refusal gate cleared —
refused_drain_capacitywould refuse a corpus above the backend's declared queue capacity.QUEUE_CAPACITYis 8 000 000 and is a bound rather than a preallocation, so 524 288 clears it. The byte cap can only ever lower the corpus below--drain-messages, so 8 M bounds any cap value.Rendering control — projected that corpus onto the committed six-backend document (respecting the validator's drain derivations) and ran chartgen. It renders every family at exit 0. The caption drops the "every window under 1 s" note and gains a two-line group:
The
parallel-vs-sequenced-64kibcanvas goes 672 → 688 of the 768 thatMAX_CAPTION_GROWTH_LINES(8) allows, so three of eight growth lines are spent and five stay spare. Every other family stays on the base 640. This is the "wider corpus deviation" #178's budget test anticipated, and it seats inside the allowance.Suite —
cargo nextest run --no-default-features -E 'binary(chartgen)'→ 128 tests run, 128 passed, 0 skipped. No document or SVG churn: the committed document is unchanged until the rerun, so the byte-compare has nothing to disagree with.What this does not fix
Of the four cells that failed "consumed before assembly" at 64 KiB / 8 consumers, in-process was one and this clears it (
unique_at_start10 500 of 524 288, 2%). The other three are RabbitMQ, whose eight-consumer group assembly ran through 37 000–46 300 messages. Putting that well under half a corpus needs ~196 k messages — 12 GiB inside an 8 GB VM. Those three are a group-assembly cost, not a corpus size, and they are expected to fail again. Recorded in the script and the runbook so the rerun is not read as having fixed them.Kafka's seven
setup_bound64 KiB rows are the same shape: they would need ~196 k messages too, which its page cache would have to hold inside the same VM.Follow-up
This is the matrix change for CAF-889 T7. The rerun itself is on the pinned host (M4 Max, per the CAF-665 host decision), so it lands separately —
scripts/bench.shper backend in the pinned order, thenscripts/bench.sh charts. Redis'sconsume_fifogap closes in that same pass via #172, per the decision to leave it as published until a full rerun.Depends-on: none — script and runbook only; the byte-cap split it builds on merged as #177, and the chart-budget headroom as #178.